package service

import (
	"context"
	"errors"
	"gorm.io/gorm"
	"sync"
	"todo-list/constants"
	"todo-list/repository/cache"
	"todo-list/repository/db/dao"
	"todo-list/repository/db/model"
	"todo-list/types"
	"todo-list/utils/auth"
	"todo-list/utils/e"
)

var (
	UserSrvIns  *UserSrv
	UserSrvOnce sync.Once
)

type UserSrv struct {
}

func (u *UserSrv) Register(ctx context.Context, req *types.UserRegisterReq) error {
	userDao := dao.NewUserDao(ctx)
	exist, err := userDao.CheckExist(0, req.UserName, req.Email)
	if exist {
		return e.New(constants.MsgUserExist)
	} else if err != nil {
		return err
	}
	user := model.User{
		UserName: req.UserName,
		NickName: req.NickName,
		Email:    req.Email,
	}
	user.SetPwd(req.Password)
	return userDao.CreateUser(&user)
}

func (u *UserSrv) Login(ctx context.Context, req *types.UserLoginReq) (*types.UserDataWithToken, error) {
	userDao := dao.NewUserDao(ctx)
	user, err := userDao.FindUserByUserNameOrEmail(req.LoginKey)
	if errors.Is(err, gorm.ErrRecordNotFound) {
		return nil, e.New(constants.MsgIncorrectPwd)
	} else if err != nil {
		return nil, err
	}
	if !user.VerifyPwd(req.Password) {
		return nil, e.New(constants.MsgIncorrectPwd)
	}
	token, err := auth.GenerateToken()
	if err != nil {
		return nil, err
	}
	err = cache.SetWithExpire(cache.TokenKey(token), user.ID, auth.TokenTimeout)
	if err != nil {
		return nil, err
	}
	err = cache.SetWithExpire(cache.TokenRefreshKey(token), user.ID, auth.RefreshTokenTimeout)
	if err != nil {
		return nil, err
	}
	return &types.UserDataWithToken{
		ID:       user.ID,
		UserName: user.UserName,
		NickName: user.NickName,
		Email:    user.Email,
		Token:    token,
	}, nil
}

func (u *UserSrv) Logout(token string) (err error) {
	// todo do something before logout, noting to do now

	err = cache.Del(cache.TokenRefreshKey(token))
	if err != nil {
		return
	}
	err = cache.Del(cache.TokenKey(token))
	return
}

func GetUserSrv() *UserSrv {
	UserSrvOnce.Do(func() {
		UserSrvIns = &UserSrv{}
	})
	return UserSrvIns
}
